﻿
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace DevelopersToolbox {
    public class PluginLoader {
        private static readonly Type PLUGIN_TYPE = typeof(IPlugin);
        private static PluginLoader s_instance;
        private Dictionary<Type, IPlugin> _loadedPlugins = new Dictionary<Type, IPlugin>();

        protected PluginLoader() {

        }

        public static PluginLoader Instance {
            get {
                if (s_instance == null) {
                    s_instance = new PluginLoader();
                }
                return s_instance;
            }
        }

        public IPlugin[] GetPlugins() {
            List<IPlugin> plugins = new List<IPlugin>();
            string[] fileNames = GetPossiblePluginFileNames();
            foreach (string fileName in fileNames) {
                IPlugin[] filePlugins = GetPlugins(fileName);
                plugins.AddRange(filePlugins);
            }
            return plugins.ToArray();
        }

        private IPlugin[] GetPlugins(string fileName) {
            List<IPlugin> plugins = new List<IPlugin>();
            Assembly assembly = Assembly.LoadFrom(fileName);
            foreach (Type type in assembly.GetExportedTypes()) {
                if (PLUGIN_TYPE.IsAssignableFrom(type)) {
                    IPlugin plugin = CreatePlugin(type);
                    if (plugin != null) {
                        plugins.Add(plugin);
                    }
                }
            }
            return plugins.ToArray();
        }

        private IPlugin CreatePlugin(Type pluginType) {
            IPlugin plugin;
            if (!_loadedPlugins.TryGetValue(pluginType, out plugin)) {
                ConstructorInfo constructor = pluginType.GetConstructor(new Type[] { });
                if (constructor == null) {
                    return null;
                }
                plugin = (IPlugin)constructor.Invoke(new object[] { });
                _loadedPlugins.Add(pluginType, plugin);
            }
            return plugin;
        }

        private string[] GetPossiblePluginFileNames() {
            List<string> files = new List<string>(Directory.GetFiles(".", "*.dll"));
            foreach (string file in files.ToArray()) {
                string fname = Path.GetFileNameWithoutExtension(file);
                if (fname.Equals("DevelopersToolbox", StringComparison.InvariantCultureIgnoreCase) ||
                    fname.Equals("DevelopersToolboxLib", StringComparison.InvariantCultureIgnoreCase)) {
                    files.Remove(file);
                }
            }
            return files.ToArray();
        }

        public IPlugin GetPlugin(string pluginAssemblyQualifiedName) {
            Type pluginType = Type.GetType(pluginAssemblyQualifiedName, false);
            IPlugin plugin = CreatePlugin(pluginType);
            return plugin;
        }

        public IPage GetPage(IPlugin plugin, Guid pageGuid) {
            foreach (IPage page in plugin.Pages) {
                if (page.Guid == pageGuid) {
                    return page;
                }
            }
            return null;
        }

        public IPage GetPage(string pluginAssemblyQualifiedName, Guid pageGuid) {
            IPlugin plugin = GetPlugin(pluginAssemblyQualifiedName);
            if (plugin == null) {
                return null;
            }
            return GetPage(plugin, pageGuid);
        }
    }
}
